r"""
Some common functions
"""

import hashlib
import os
import json
from io import BytesIO
import pkg_resources
from collections import defaultdict

from PIL import ImageFont


DEFAULT_FONT_PATH = pkg_resources.resource_filename(__name__, 'assets/DFPKingGothicGB-Light-2.ttf')


def str_md5(bytes_):
    """Compute string md5 value"""
    md5 = hashlib.md5()
    md5.update(bytes_)
    return md5.hexdigest()


def image_md5(image):
    """Compute a image md5 value"""
    bio = BytesIO()
    image.save(bio, "JPEG")
    val = bio.getvalue()
    name_hd = str_md5(val)
    del bio
    return name_hd


def get_default_font(size=24):
    """get a default PIL.ImageFont instance for show label name on image
    which could deal with both English and Chinese

    Args:
        size(int): font size
    Returns:
        PIL.ImageFont: a ImageFont instance
    """
    return ImageFont.truetype(DEFAULT_FONT_PATH, size)
    

def load_json(path, *, encoding=None, **kwargs):
    """Load json format file"""
    with open(path, encoding=encoding) as f:
        return json.load(f, **kwargs)


def save_json(obj, path, indent=2, ensure_ascii=False, *args, **kwargs):
    """Save object as json format"""
    with open(path, "w") as f:
        json.dump(obj, f, indent=indent, ensure_ascii=ensure_ascii,
                  *args, **kwargs)


def group(lst, key):
    """Group list by key"""
    if callable(key):
        key = list(map(key, lst))
    res = defaultdict(list)
    for v, k in zip(lst, key):
        res[k].append(v)
    return res


def basename_head(path):
    """Get basename head of a path """
    return os.path.basename(path).rsplit(".", 1)[0]


def align_paths(paths0, paths1, *args):
    """ Align paths base on its name head
        This function will delete dis-matched paths with no promot.
        Args:
            paths0: the first of list file paths
            paths1: the second of list file paths
            args: others of list file paths
    """
    paths_list = [paths0, paths1] + list(args)
    heads_list = [[basename_head(p) for p in paths]
                  for paths in paths_list]
    head_path_map_list = [dict(zip(heads, paths))
                          for heads, paths in zip(heads_list, paths_list)]

    valid_heads = set(heads_list[0])
    for hs in heads_list[1:]:
        valid_heads = valid_heads.intersection(hs)

    aligned_paths_list = [[mp[h] for h in valid_heads]
                          for mp in head_path_map_list]

    return aligned_paths_list


def ifind_file_recursive(directory, suffixes, ignore_case=False):
    """ Find all file with set postfixes

    :param directory: the target directory
    :param suffixes: a suffix or a list of suffixes of file to be find
    :param ignore_case: match file suffix in case angnose mode
    :return: a generator which returns the path to the file which meets
        the postfix
    """
    if not os.path.isdir(directory):
        raise FileNotFoundError("[Errno 2] No such file or directory: '%s'" % directory)

    if isinstance(suffixes, str):
        suffixes = [suffixes]

    def _suffix_match(filename):
        if ignore_case:
            return any([filename.lower().endswith(x.lower()) for x in suffixes])
        else:
            return any([filename.endswith(x) for x in suffixes])

    for root, dirs, files in os.walk(directory):
        for file in files:
            if _suffix_match(file):
                yield os.path.join(root, file)


def find_file_recursive(directory, suffixes, ignore_case=False):
    """ Find all file with set postfixes

    :param directory: the target directory
    :param suffixes: a suffix or a list of suffixes of file to be find
    :param ignore_case: match file suffix in case angnose mode
    :return: a list which returns the path to the file which meets
        the postfix
    """
    return list(ifind_file_recursive(directory, suffixes, ignore_case=ignore_case))


def collect_pascal_data(directory):
    """ Collect pascal format dataset """
    img_ps = find_file_recursive(directory, (".jpg", ".jpeg", ".png", ".bmp"), ignore_case=True)
    ann_ps = find_file_recursive(directory, ".xml", ignore_case=True)
    img_ps, ann_ps = align_paths(img_ps, ann_ps)
    return img_ps, ann_ps
